home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume15 / vtree < prev    next >
Encoding:
Internet Message Format  |  1988-05-23  |  22.9 KB

  1. Subject:  v15i005:  Visual display of directory tree
  2. Newsgroups: comp.sources.unix
  3. Sender: sources
  4. Approved: rsalz@uunet.UU.NET
  5.  
  6. Submitted-by: uunet!ispi!jbayer
  7. Posting-number: Volume 15, Issue 5
  8. Archive-name: vtree
  9.  
  10.   Following is a shar archive containing a utility I have developed
  11.   for Un*x systems.  It displays the structure of a directory tree or
  12.   filesystem.  Unshar and read the README file, and the manual page.
  13.   This was developed on a Xenix 386 system, but should work on most
  14.   Un*x systems since it uses code from another public-domain program
  15.   which already works on other systems.  Credits are given in both
  16.   the README file and the manual page.
  17.  
  18.     Enjoy.
  19.  
  20.     Jonathan Bayer
  21.     Intelligent Software Products, Inc.
  22.     19 Virginia Ave.
  23.     Rockville Centre, NY   11570
  24.     uunet!ispi!jbayer        (via uunet)
  25.  
  26. [  As the documentation says, if you don't have the directory access
  27.    routines, you should get them from your nearest archive site.  -r$  ]
  28.  
  29. #! /bin/sh
  30. # This is a shell archive, meaning:
  31. # 1. Remove everything above the #! /bin/sh line.
  32. # 2. Save the resulting text in a file.
  33. # 3. Execute the file with /bin/sh (not csh) to create the files:
  34. #    Makefile
  35. #    README
  36. #    customize.h
  37. #    direct.c
  38. #    hash.c
  39. #    hash.h
  40. #    patchlevel.h
  41. #    vtree.1
  42. #    vtree.c
  43. export PATH; PATH=/bin:$PATH
  44. if test -f 'Makefile'
  45. then
  46.     echo shar: will not over-write existing file "'Makefile'"
  47. else
  48. cat << \SHAR_EOF > 'Makefile'
  49. #    Build VTREE
  50. #
  51. #    Define the type of system we're working with.  Three
  52. # choices:
  53. #
  54. #   1.    BSD Unix 4.2 or 4.3.  Directory read support in the
  55. # standard library, so we don't have to do much.  Select BSD.
  56. #
  57. #   2.    System V.  I depend on Doug Gwyn's directory reading
  58. # routines.  They were posted to Usenet "comp.sources" early in
  59. # May 1987.  They're worth the effort to get, if you don't have
  60. # them already.  Select SYS_V.  Be sure to define NLIB to be the
  61. # 'cc' option to include the directory library.
  62. #
  63. #   3.  System III, or machines without any directory read
  64. # packages.  I have a minimal kludge.  Select SYS_III.
  65. #
  66.  
  67. # Case 1:
  68. #SYS=    -DBSD
  69. #NLIB=
  70.  
  71. # Case 2:
  72. SYS=    -DSYS_V
  73. NLIB=    -lndir
  74.  
  75. # Case 3:
  76. #SYS=    -DSYS_III
  77. #NLIB=
  78.  
  79.  
  80. #    Standard things you may wish to change:
  81. #
  82. #    INSTALL        directory to install vtree in
  83.  
  84. INSTALL    =    /usr/local/bin
  85.  
  86.  
  87. #    The following OPTIONS may be defined:
  88. #
  89. #    LSTAT        we have the lstat(2) system call (BSD only)
  90. #    HSTATS        print hashing statistics at end of run
  91. #
  92. #    Define LSTAT, HSTATS here
  93.  
  94. OPTIONS    = 
  95.  
  96. #  END OF USER-DEFINED OPTIONS
  97.  
  98.  
  99. CFLAGS=    -O $(SYS) $(OPTIONS)
  100. SRCS=        vtree.c    hash.c    direct.c    \
  101.         hash.h    customize.h    patchlevel.h
  102. OBJS=        vtree.o    hash.o
  103.  
  104. vtree:        $(OBJS)
  105.         cc -o vtree $(CFLAGS) $(OBJS) $(NLIB)
  106.  
  107. install:    vtree
  108.         cp vtree $(INSTALL)
  109.         chown local $(INSTALL)/vtree
  110.         chgrp pd $(INSTALL)/vtree
  111.         chmod 755 $(INSTALL)/vtree
  112.  
  113. clean:
  114.         rm -f $(OBJS) vtree *~
  115.  
  116. vtree.o:    vtree.c direct.c hash.h customize.h patchlevel.h
  117.  
  118. hash.o:        hash.c hash.h customize.h patchlevel.h
  119. SHAR_EOF
  120. fi # end of overwriting check
  121. if test -f 'README'
  122. then
  123.     echo shar: will not over-write existing file "'README'"
  124. else
  125. cat << \SHAR_EOF > 'README'
  126.     This is the first release of the VTREE (please pronounce this
  127. V-TREE, for "visual files") program.  The program is designed to show
  128. the layout of a directory tree or filesystem.  It has options to show
  129. the amount of storage being taken up in each directory, count the number
  130. of inodes, etc.
  131.  
  132.     VTREE is dependent on the UCB directory reading routines. 
  133. Public-domain routines for System V have been released to the Usenet
  134. (comp.sources.unix) by Doug Gwyn (gwyn@brl.mil).  If you don't have
  135. them, they're worth your trouble to get.  Still, you may be able to use
  136. the System III configuration of the Makefile as a stopgap measure. 
  137.  
  138.  
  139.     The program was originally the program AGEF, written by David S.
  140. Hayes.  As it stands now the hashing routines are untouched by myself,
  141. but most of the rest of the program is different.  The System III
  142. routines are also his.
  143.  
  144.  
  145.     I hope this program will be useful to you.  If you find bugs in
  146. it or have any suggestions for improvements, I'd like to hear about
  147. them.
  148.  
  149. Jonathan B. Bayer
  150. Intelligent Software Products, Inc.
  151. Rockville Centre, NY   11570
  152. Phone: (516) 766-2867
  153. usenet:    uunet!ispi!root
  154.  
  155. SHAR_EOF
  156. fi # end of overwriting check
  157. if test -f 'customize.h'
  158. then
  159.     echo shar: will not over-write existing file "'customize.h'"
  160. else
  161. cat << \SHAR_EOF > 'customize.h'
  162. /* agef
  163.   
  164.    SCCS ID    @(#)customize.h    1.6    7/9/87
  165.   
  166.    This is the customizations file.  It changes our ideas of
  167.    how to read directories.
  168. */
  169.  
  170. /*#define FLOAT_FORMAT    "%d %#4.1fM"    /* if your printf does %# */
  171. #define FLOAT_FORMAT    "%d %4.1fM" /* if it doesn't do %# */
  172.  
  173. #define NAMELEN    512        /* max size of a full pathname */
  174.  
  175. #ifdef BSD
  176. #    include        <sys/dir.h>
  177. #    define    OPEN    DIR
  178. #    define    READ    struct direct
  179. #    define    NAME(x)    ((x).d_name)
  180. #else
  181. #ifdef SYS_V
  182.  /* Customize this.  This is part of Doug Gwyn's package for */
  183.  /* reading directories.  If you've put this file somewhere */
  184.  /* else, edit the next line. */
  185.  
  186. #    include        <sys/dirent.h>
  187.  
  188. #    define    OPEN    struct direct
  189. #    define    READ    struct dirent
  190. #    define    NAME(x)    ((x).d_name)
  191. #else
  192. #ifdef SYS_III
  193. #    define    OPEN    FILE
  194. #    define    READ    struct direct
  195. #    define    NAME(x)    ((x).d_name)
  196. #    define    INO(x)    ((x).d_ino)
  197.  
  198. #    include        "direct.c"
  199.  
  200. #endif
  201. #endif
  202. #endif
  203. SHAR_EOF
  204. fi # end of overwriting check
  205. if test -f 'direct.c'
  206. then
  207.     echo shar: will not over-write existing file "'direct.c'"
  208. else
  209. cat << \SHAR_EOF > 'direct.c'
  210. /* direct.c
  211.   
  212.    SCCS ID    @(#)direct.c    1.6    7/9/87
  213.   
  214.  *
  215.  *    My own substitution for the berkeley reading routines,
  216.  *    for use on System III machines that don't have any other
  217.  *    alternative.
  218.  */
  219.  
  220. #define NAMELENGTH    14
  221. #define opendir(name)    fopen(name, "r")
  222. #define closedir(fp)    fclose(fp)
  223.  
  224. struct dir_entry {        /* What the system uses internally. */
  225.     ino_t           d_ino;
  226.     char            d_name[NAMELENGTH];
  227. };
  228.  
  229. struct direct {            /* What these routines return. */
  230.     ino_t           d_ino;
  231.     char            d_name[NAMELENGTH];
  232.     char            terminator;
  233. };
  234.  
  235.  
  236.  /*
  237.   * Read a directory, returning the next (non-empty) slot. 
  238.   */
  239.  
  240. READ           *
  241. readdir(dp)
  242.     OPEN           *dp;
  243. {
  244.     static READ     direct;
  245.  
  246.     /* This read depends on direct being similar to dir_entry. */
  247.  
  248.     while (fread(&direct, sizeof(struct dir_entry), 1, dp) != 0) {
  249.     direct.terminator = '\0';
  250.     if (INO(direct) != 0)
  251.         return &direct;
  252.     };
  253.  
  254.     return (READ *) NULL;
  255. }
  256. SHAR_EOF
  257. fi # end of overwriting check
  258. if test -f 'hash.c'
  259. then
  260.     echo shar: will not over-write existing file "'hash.c'"
  261. else
  262. cat << \SHAR_EOF > 'hash.c'
  263. /* hash.c
  264.   
  265.    SCCS ID    @(#)hash.c    1.6    7/9/87
  266.   
  267.  * Hash table routines for AGEF.  These routines keep the program from
  268.  * counting the same inode twice.  This can happen in the case of a
  269.  * file with multiple links, as in a news article posted to several
  270.  * groups.  The use of a hashing scheme was suggested by Anders
  271.  * Andersson of Uppsala University, Sweden.  (enea!kuling!andersa) 
  272.  */
  273.  
  274. /* hash.c change history:
  275.  28 March 1987        David S. Hayes (merlin@hqda-ai.UUCP)
  276.     Initial version.
  277. */
  278.  
  279. #include <stdio.h>
  280. #include <sys/types.h>
  281. #include "hash.h"
  282.  
  283.  
  284. static struct htable *tables[TABLES];
  285.  
  286. /* These are for statistical use later on. */
  287. static int      hs_tables = 0,    /* number of tables allocated */
  288.                 hs_duplicates = 0,    /* number of OLD's returned */
  289.                 hs_buckets = 0,    /* number of buckets allocated */
  290.                 hs_extensions = 0,    /* number of bucket extensions */
  291.                 hs_searches = 0,/* number of searches */
  292.                 hs_compares = 0,/* total key comparisons */
  293.                 hs_longsearch = 0;    /* longest search */
  294.  
  295.  
  296.  /*
  297.   * This routine takes in a device/inode, and tells whether it's been
  298.   * entered in the table before.  If it hasn't, then the inode is added
  299.   * to the table.  A separate table is maintained for each major device
  300.   * number, so separate file systems each have their own table. 
  301.   */
  302.  
  303. h_enter(dev, ino)
  304.     dev_t           dev;
  305.     ino_t           ino;
  306. {
  307.     static struct htable *tablep = (struct htable *) 0;
  308.     register struct hbucket *bucketp;
  309.     register ino_t *keyp;
  310.     int             i;
  311.  
  312.     hs_searches++;        /* stat, total number of calls */
  313.  
  314.     /*
  315.      * Find the hash table for this device. We keep the table pointer
  316.      * around between calls to h_enter, so that we don't have to locate
  317.      * the correct hash table every time we're called.  I don't expect
  318.      * to jump from device to device very often. 
  319.      */
  320.     if (!tablep || tablep->device != dev) {
  321.     for (i = 0; tables[i] && tables[i]->device != dev;)
  322.         i++;
  323.     if (!tables[i]) {
  324.         tables[i] = (struct htable *) malloc(sizeof(struct htable));
  325.         if (tables[i] == NULL) {
  326.         perror("can't malloc hash table");
  327.         return NEW;
  328.         };
  329. /*        bzero(tables[i], sizeof(struct htable)); */
  330.         memset((char *) tables[i], '\0', sizeof (struct htable));
  331.         tables[i]->device = dev;
  332.         hs_tables++;    /* stat, new table allocated */
  333.     };
  334.     tablep = tables[i];
  335.     };
  336.  
  337.     /* Which bucket is this inode assigned to? */
  338.     bucketp = &tablep->buckets[ino % BUCKETS];
  339.  
  340.     /*
  341.      * Now check the key list for that bucket.  Just a simple linear
  342.      * search. 
  343.      */
  344.     keyp = bucketp->keys;
  345.     for (i = 0; i < bucketp->filled && *keyp != ino;)
  346.     i++, keyp++;
  347.  
  348.     hs_compares += i + 1;    /* stat, total key comparisons */
  349.  
  350.     if (i && *keyp == ino) {
  351.     hs_duplicates++;    /* stat, duplicate inodes */
  352.     return OLD;
  353.     };
  354.  
  355.     /* Longest search.  Only new entries could be the longest. */
  356.     if (bucketp->filled >= hs_longsearch)
  357.     hs_longsearch = bucketp->filled + 1;
  358.  
  359.     /* Make room at the end of the bucket's key list. */
  360.     if (bucketp->filled == bucketp->length) {
  361.     /* No room, extend the key list. */
  362.     if (!bucketp->length) {
  363.         bucketp->keys = (ino_t *) calloc(EXTEND, sizeof(ino_t));
  364.         if (bucketp->keys == NULL) {
  365.         perror("can't malloc hash bucket");
  366.         return NEW;
  367.         };
  368.         hs_buckets++;
  369.     } else {
  370.         bucketp->keys = (ino_t *)
  371.         realloc(bucketp->keys,
  372.             (EXTEND + bucketp->length) * sizeof(ino_t));
  373.         if (bucketp->keys == NULL) {
  374.         perror("can't extend hash bucket");
  375.         return NEW;
  376.         };
  377.         hs_extensions++;
  378.     };
  379.     bucketp->length += EXTEND;
  380.     };
  381.  
  382.     bucketp->keys[++(bucketp->filled)] = ino;
  383.     return NEW;
  384. }
  385.  
  386.  
  387.  /* Buffer statistics functions.  Print 'em out. */
  388.  
  389. #ifdef HSTATS
  390. void
  391. h_stats()
  392. {
  393.     fprintf(stderr, "\nHash table management statistics:\n");
  394.     fprintf(stderr, "  Tables allocated: %d\n", hs_tables);
  395.     fprintf(stderr, "  Buckets used: %d\n", hs_buckets);
  396.     fprintf(stderr, "  Bucket extensions: %d\n\n", hs_extensions);
  397.     fprintf(stderr, "  Total searches: %d\n", hs_searches);
  398.     fprintf(stderr, "  Duplicate keys found: %d\n", hs_duplicates);
  399.     if (hs_searches)
  400.     fprintf(stderr, "  Average key search: %d\n",
  401.         hs_compares / hs_searches);
  402.     fprintf(stderr, "  Longest key search: %d\n", hs_longsearch);
  403.     fflush(stderr);
  404. }
  405.  
  406. #endif
  407. SHAR_EOF
  408. fi # end of overwriting check
  409. if test -f 'hash.h'
  410. then
  411.     echo shar: will not over-write existing file "'hash.h'"
  412. else
  413. cat << \SHAR_EOF > 'hash.h'
  414. /* Defines for the agef hashing functions.
  415.  
  416.    SCCS ID    @(#)hash.h    1.6    7/9/87
  417.  */
  418.  
  419. #define BUCKETS        257    /* buckets per hash table */
  420. #define TABLES        50    /* hash tables */
  421. #define EXTEND        100    /* how much space to add to a bucket */
  422.  
  423. struct hbucket {
  424.     int             length;    /* key space allocated */
  425.     int             filled;    /* key space used */
  426.     ino_t          *keys;
  427. };
  428.  
  429. struct htable {
  430.     dev_t           device;    /* device this table is for */
  431.     struct hbucket  buckets[BUCKETS];    /* the buckets of the table */
  432. };
  433.  
  434. #define OLD    0        /* inode was in hash already */
  435. #define NEW    1        /* inode has been added to hash */
  436. SHAR_EOF
  437. fi # end of overwriting check
  438. if test -f 'patchlevel.h'
  439. then
  440.     echo shar: will not over-write existing file "'patchlevel.h'"
  441. else
  442. cat << \SHAR_EOF > 'patchlevel.h'
  443. /* Patchlevel for VTREE
  444.  
  445. */
  446.  
  447. #define PATCHLEVEL     V1.0
  448. #define    VERSION        "VTREE    1.0    4/26/88\n"
  449. SHAR_EOF
  450. fi # end of overwriting check
  451. if test -f 'vtree.1'
  452. then
  453.     echo shar: will not over-write existing file "'vtree.1'"
  454. else
  455. cat << \SHAR_EOF > 'vtree.1'
  456. .TH VTREE 1 local
  457. .SH NAME
  458. vtree \- print a visual tree of a directory structure
  459. .SH SYNOPSIS
  460. .B vtree
  461. [ \-d] [ \-h # ] [ \-i ] [ \-s ] [ \-q ] [ \-v ] [ \-V ] 
  462. .SH DESCRIPTION
  463. .IP 
  464. Vtree is a program which scans directories/filesystems and displays the structure on the
  465. standard output.   Normally it will ignore duplicate inodes.
  466. .IP "\-d "
  467. Instructs the program to include the duplicate inodes in the totals.
  468. .PP
  469. .IP "\-h #"
  470. Specifies how many levels down to display.
  471. .PP
  472. .IP \-i 
  473. displays the number of inodes (excluding directories) in each directory 
  474. .PP
  475. .IP \-s 
  476. Instructs the program to continue counting inodes and file sizes when it
  477. has exceeded the levels specified.
  478. .PP
  479. .IP \-t 
  480. Displays totals at the end of the report
  481. .PP
  482. .IP \-q
  483. Quick display.  No totals of any kind are kept.
  484. .PP
  485. .IP \-v
  486. Visual display.  Normally the program displays one directory on a line,
  487. indenting lines to indicate subdirectories.  The visual display builds
  488. a tree on the screen showing the actual directory structure.  This method
  489. of display excludes any totals other than the final totals.
  490. .PP
  491. .IP \-V
  492. Shows current version.  Specifying 2 Vs (-VV) will also show all options in
  493. force.
  494. .SH AUTHOR
  495. Jonathan B. Bayer
  496. .PP
  497. Intelligent Software Products, Inc.
  498. .PP
  499. Rockville Centre, NY   11570
  500. .SH ACKNOWLEDGMENTS
  501. The program uses the directory routines written and released to the
  502. public domain by Doug Gwyn.
  503. The program is originally based on a program called AGEF written by
  504. David S. Hayes.
  505. SHAR_EOF
  506. fi # end of overwriting check
  507. if test -f 'vtree.c'
  508. then
  509.     echo shar: will not over-write existing file "'vtree.c'"
  510. else
  511. cat << \SHAR_EOF > 'vtree.c'
  512. /* vtree
  513.   
  514.    +=======================================+
  515.    | This program is in the public domain. |
  516.    +=======================================+
  517.   
  518.    This program shows the directory structure of a filesystem or 
  519.    part of one.  It also shows the amount of space taken up by files
  520.    in each subdirectory. 
  521.   
  522.    Call via
  523.   
  524.     vtree fn1 fn2 fn3 ...
  525.   
  526.    If any of the given filenames is a directory (the usual case),
  527.    vtree will recursively descend into it, and the output line will
  528.    reflect the accumulated totals for all files in the directory.
  529.    
  530.    This program is based upon "agef" written by David S. Hayes at the 
  531.    Army Artificial Intelligence Center at the Pentagon.
  532.    
  533.    This program is dependent upon the new directory routines written by
  534.    Douglas A. Gwyn at the US Army Ballistic Research Laboratory at the
  535.    Aberdeen Proving Ground in Maryland.
  536. */
  537.  
  538. #include "patchlevel.h"
  539.  
  540. #include <ctype.h>
  541. #include <sys/types.h>
  542. #include <sys/stat.h>
  543. #include <sys/param.h>
  544. #include <stdio.h>
  545.  
  546. #include "customize.h"
  547. #include "hash.h"
  548.  
  549. #define SAME        0    /* for strcmp */
  550. #define BLOCKSIZE    512    /* size of a disk block */
  551.  
  552. #define K(x)        ((x + 1023)/1024)    /* convert stat(2) blocks into
  553.                      * k's.  On my machine, a block
  554.                      * is 512 bytes. */
  555.  
  556. #define    TRUE    1
  557. #define    FALSE    0
  558. #define    V_CHAR    "|"    /*    Vertical character    */
  559. #define    H_CHAR    "-"    /*    Horizontal character    */
  560. #define    A_CHAR    ">"    /*    Arrow char        */
  561. #define    T_CHAR    "+"    /*    Tee char        */
  562. #define    L_CHAR    "\\"    /*    L char, bottom of a branch    */
  563.  
  564. #define    MAX_COL_WIDTH    15
  565. #define    MAX_V_DEPTH    256        /* max depth for visual display */
  566.  
  567. int        indent = 0,        /* current indent */
  568.         depth = 9999,        /* max depth */
  569.         cur_depth = 0,    
  570.         sum = FALSE,        /* sum the subdirectories */
  571.         dup = FALSE,        /* use duplicate inodes */
  572.         cnt_inodes = FALSE,    /* count inodes */
  573.         quick = FALSE;        /* quick display */
  574.         visual = FALSE;        /* visual display */
  575.         version = 0;        /* = 1 display version, = 2 show options */
  576.         sub_dirs[MAX_V_DEPTH];
  577.  
  578. struct    stat    stb;            /* Normally not a good idea, but */
  579.                     /* this structure is used through- */
  580.                     /* out the program           */
  581.  
  582. extern char    *optarg;            /* from getopt(3) */
  583. extern int      optind,
  584.                 opterr;
  585.  
  586.  
  587. char           *Program;        /* our name */
  588. short           sw_follow_links = 1;    /* follow symbolic links */
  589. short           sw_summary;        /* print Grand Total line */
  590.  
  591. int             total_inodes, inodes;    /* inode count */
  592. long            total_sizes, sizes;    /* block count */
  593.  
  594. char            topdir[NAMELEN];    /* our starting directory */
  595.  
  596.  
  597.  /*
  598.   * We ran into a subdirectory.  Go down into it, and read everything
  599.   * in there. 
  600.   */
  601. int    indented = FALSE;    /* These had to be global since they */
  602. int    last_indent = 0;    /* determine what gets displayed during */
  603.                 /* the visual display */
  604.  
  605. down(subdir)
  606. char    *subdir;
  607. {
  608. OPEN    *dp;            /* stream from a directory */
  609. OPEN    *opendir ();
  610. char    cwd[NAMELEN];
  611. READ    *file;            /* directory entry */
  612. READ    *readdir ();
  613. int    i;
  614. struct    stat    stb;
  615.  
  616.     if ( (cur_depth == depth) && (!sum) )
  617.         return;
  618.  
  619. /* display the tree */
  620.  
  621.     if (cur_depth < depth) {
  622.         if (visual) {
  623.             if (!indented) {
  624.                 for (i = 1; i <cur_depth; i++) {
  625.                     if (sub_dirs[i]) {
  626.                         printf("%*s%s   ",MAX_COL_WIDTH-4," ",V_CHAR);
  627.                     }
  628.                     else printf("%*s   ",MAX_COL_WIDTH-3," ");
  629.                 }
  630.                 if (cur_depth>0) {
  631.                     if (sub_dirs[cur_depth] == 0) {
  632.                         printf("%*s%s%s%s ",MAX_COL_WIDTH-4," ",L_CHAR,H_CHAR,A_CHAR);
  633.                     }
  634.                     else printf("%*s%s%s%s ",MAX_COL_WIDTH-4," ",T_CHAR,H_CHAR,A_CHAR);
  635.                 }
  636.             } else {
  637.                 for (i = 1; i<MAX_COL_WIDTH-last_indent-3; i++)
  638.                     printf("%s",H_CHAR);
  639.                 printf("%s%s%s ",T_CHAR,H_CHAR,A_CHAR);
  640.             }
  641.  
  642. /* This will only happen on the first entry into this routine */
  643.  
  644.             if (strlen(subdir) > MAX_COL_WIDTH - 4) {
  645.                 printf("%s\n",subdir);
  646.                 printf("%s ",&subdir[strlen(subdir)-MAX_COL_WIDTH+5]);
  647.                 last_indent = MAX_COL_WIDTH - 4;
  648.             }
  649.             else {
  650.                 printf("%s ",subdir);
  651.                 last_indent = strlen(subdir)+1;
  652.             }
  653.             indented = TRUE;
  654.         }
  655.         else printf("%*s%s",indent," ",subdir);
  656.     }
  657.  
  658. /* open subdirectory */
  659.  
  660.     if ((dp = opendir(subdir)) == NULL) {
  661.         printf(" - can't read %s\n", subdir);
  662.         indented = FALSE;
  663.         return;
  664.     }
  665.  
  666.     cur_depth++;
  667.     indent+=3;
  668.  
  669.     getcwd(cwd, sizeof(cwd));        /* remember where we are */
  670.     chdir(subdir);                /* go into subdir */
  671.     if ( (!quick) && (!visual) ) {
  672.  
  673. /* accumulate total sizes and inodes in current directory */
  674.  
  675.         for (file = readdir(dp); file != NULL; file = readdir(dp))
  676.             if (strcmp(NAME(*file), "..") != SAME) 
  677.                 get_data(NAME(*file),FALSE);
  678.  
  679.         if (cur_depth<depth) {
  680.             if (cnt_inodes) printf("   %d",inodes);
  681.             printf(" : %ld\n",sizes);
  682.             total_sizes += sizes;
  683.             total_inodes += inodes;
  684.             sizes = 0;
  685.             inodes = 0;
  686.         }
  687.         rewinddir(dp);
  688.     } else if (!visual) printf("\n");
  689.  
  690.     if (visual) {
  691.  
  692. /* count subdirectories */
  693.  
  694.         for (file = readdir(dp); file != NULL; file = readdir(dp)) {
  695.             if ( (strcmp(NAME(*file), "..") != SAME) &&
  696.                  (strcmp(NAME(*file), ".") != SAME) ) {
  697.                 if (chk_4_dir(NAME(*file))) {
  698.                     sub_dirs[cur_depth]++;
  699.                 }
  700.             }
  701.         }
  702.         rewinddir(dp);
  703.     }
  704.     
  705. /* go down into the subdirectory */
  706.  
  707.     for (file = readdir(dp); file != NULL; file = readdir(dp)) {
  708.         if ( (strcmp(NAME(*file), "..") != SAME) &&
  709.              (strcmp(NAME(*file), ".") != SAME) ) {
  710.             if (chk_4_dir(NAME(*file))) 
  711.                 sub_dirs[cur_depth]--;
  712.             get_data(NAME(*file),TRUE);
  713.              }
  714.     }
  715.  
  716.     if ( (!quick) && (!visual) ) {
  717.  
  718. /* print totals */
  719.  
  720.         if (cur_depth == depth) {
  721.             if (cnt_inodes) printf("   %d",inodes);
  722.             printf(" : %ld\n",sizes);
  723.             total_sizes += sizes;
  724.             total_inodes += inodes;
  725.             sizes = 0;
  726.             inodes = 0;
  727.         }
  728.     } else if (visual && indented) {
  729.         printf("\n");
  730.         indented = FALSE;
  731.     }
  732.  
  733.     indent-=3;
  734.     sub_dirs[cur_depth] = 0;
  735.     cur_depth--;
  736.  
  737.     chdir(cwd);            /* go back where we were */
  738.     closedir(dp);            /* shut down the directory */
  739. } /* down */
  740.  
  741.  
  742.  
  743. int    chk_4_dir(path)
  744. char    *path;
  745. {
  746.     if (is_directory(path)) return TRUE;
  747.     else return FALSE;
  748.         
  749. } /* chk_4_dir */
  750.  
  751.  
  752.  
  753. /* Is the specified path a directory ? */
  754.  
  755. int    is_directory(path)
  756. char           *path;
  757. {
  758.  
  759. #ifdef LSTAT
  760.     if (sw_follow_links)
  761.         stat(path, &stb);    /* follows symbolic links */
  762.     else
  763.         lstat(path, &stb);    /* doesn't follow symbolic links */
  764. #else
  765.     stat(path, &stb);
  766. #endif
  767.  
  768.     if ((stb.st_mode & S_IFMT) == S_IFDIR)
  769.         return TRUE;
  770.     else return FALSE;
  771. } /* is_directory */
  772.  
  773.  
  774.  
  775.  /*
  776.   * Get the aged data on a file whose name is given.  If the file is a
  777.   * directory, go down into it, and get the data from all files inside. 
  778.   */
  779.  
  780. get_data(path,cont)
  781. char           *path;
  782. int        cont;    
  783. {
  784. /* struct    stat    stb; */
  785. int        i;
  786.  
  787.     if (cont) { 
  788.         if (is_directory(path)) 
  789.             down(path);
  790.     }
  791.     else {
  792.         if (is_directory(path)) return;
  793.  
  794.             /* Don't do it again if we've already done it once. */
  795.  
  796.         if ( (h_enter(stb.st_dev, stb.st_ino) == OLD) && (!dup) )
  797.             return;
  798.         inodes++;
  799.         sizes+= K(stb.st_size);
  800.     }
  801. } /* get_data */
  802.  
  803.  
  804.  
  805. main(argc, argv)
  806. int    argc;
  807. char    *argv[];
  808. {
  809. int    i,
  810.     j,
  811.     err = FALSE;
  812. int    option;
  813. int    user_file_list_supplied = 0;
  814.  
  815.     Program = *argv;        /* save our name for error messages */
  816.  
  817.     /* Pick up options from command line */
  818.  
  819.     while ((option = getopt(argc, argv, "dh:istqvV")) != EOF) {
  820.         switch (option) {
  821.             case 'h':    depth = atoi(optarg);
  822.                     while (*optarg) {
  823.                         if (!isdigit(*optarg)) {
  824.                             err = TRUE;
  825.                             break;
  826.                         }
  827.                         optarg++;
  828.                     }
  829.                     break;
  830.             case 'd':    dup = TRUE;
  831.                     break;    
  832.             case 'i':    cnt_inodes = TRUE;
  833.                     break;        
  834.             case 's':    sum = TRUE;
  835.                     break;
  836.             case 't':    sw_summary = TRUE;
  837.                     break;
  838.             case 'q':    quick = TRUE;
  839.                     dup = FALSE;
  840.                     sum = FALSE;
  841.                     cnt_inodes = FALSE;
  842.                     break;
  843.             case 'v':    visual = TRUE;
  844.                     break;
  845.             case 'V':    version++;
  846.                     break;
  847.             default:    err = TRUE;
  848.         }
  849.         if (err) {
  850.             fprintf(stderr,"%s: [ -d ] [ -h # ] [ -i ] [ -s ] [ -q ] [ -v ] [ -V ]\n",Program);
  851.             fprintf(stderr,"    -d    count duplicate inodes\n");
  852.             fprintf(stderr,"    -h #    height of tree to look at\n");
  853.             fprintf(stderr,"    -i    count inodes\n");
  854.             fprintf(stderr,"    -s    include subdirectories not shown due to -h option\n");
  855.             fprintf(stderr,"    -t    totals at the end\n");
  856.             fprintf(stderr,"    -q    quick display, no counts\n");
  857.             fprintf(stderr,"    -v    visual display\n");
  858.             fprintf(stderr,"    -V    show current version\n");
  859.             fprintf(stderr,"        (2 Vs shows specified options)\n");
  860.             exit(-1);
  861.         }
  862.     
  863.     }
  864.  
  865.     if (version > 0 ) {
  866.         printf("%s",VERSION);
  867.         if (version>1) {
  868.             printf("Tree height:    %d\n",depth);
  869.             if (dup) printf("Include duplicate inodes\n");
  870.             if (cnt_inodes) printf("Count inodes\n");
  871.             if (sum) printf("Include unseen subdirectories in totals\n");
  872.             if (sw_summary) printf("Print totals at end\n");
  873.             if (quick) printf("Quick display only\n");
  874.             if (visual) printf("Visual tree\n");
  875.         }
  876.     }
  877.     
  878.     /* If user didn't specify targets, inspect current directory. */
  879.  
  880.     if (optind >= argc) {
  881.         user_file_list_supplied = 0;
  882.     } else {
  883.         user_file_list_supplied = 1;
  884.     }
  885.  
  886.     getcwd(topdir, sizeof (topdir));        /* find out where we are */
  887.  
  888.     /* Zero out grand totals */
  889.     total_inodes = total_sizes = 0;
  890.     /* Zero out sub_dirs */
  891.     for (i=0; i<=MAX_V_DEPTH; i++) {
  892.         sub_dirs[i] = 0;
  893.     }
  894.         
  895.     /* Inspect each argument */
  896.     for (i = optind; i < argc || (!user_file_list_supplied && i == argc); i++) {
  897.         cur_depth = inodes = sizes = 0;
  898.  
  899.         chdir(topdir);        /* be sure to start from the same place */
  900.         get_data(user_file_list_supplied?argv[i] : topdir, TRUE);/* this may change our cwd */
  901.  
  902.         total_inodes += inodes;
  903.         total_sizes += sizes;
  904.     }
  905.  
  906.     if (sw_summary) {
  907.         printf("\n\nTotal space used: %ld\n",total_sizes);
  908.         if (cnt_inodes) printf("Total inodes: %d\n",inodes);
  909.     }
  910.     
  911. #ifdef HSTATS
  912.     fflush(stdout);
  913.     h_stats();
  914. #endif
  915.  
  916.     exit(0);
  917. } /* main */
  918.  
  919.  
  920. SHAR_EOF
  921. fi # end of overwriting check
  922. #    End of shell archive
  923. exit 0
  924.  
  925.  
  926.